/* Speech Recognition Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "app_speech_if.h"

#include "sdkconfig.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"

#include "lwip/err.h"
#include "lwip/sys.h"
#include "lwip/sockets.h"
#include <lwip/netdb.h>
#include "cJSON.h"

#include "led_rgb.h"
#include "board_moonlight.h"
#include "blufi.h"
#include "iot_button.h"

#include "sensor.h"

#define PORT 3333
extern const int CONNECTED_BIT ;
static uint8_t g_red;
static uint8_t g_green;
static uint8_t g_blue;
static const char *TAG = "moonlight";

static led_rgb_t *g_leds = NULL;
typedef struct {
    uint8_t r;
    uint8_t g;
    uint8_t b;

} led_color_t;

static led_color_t led_color = {0};
static led_color_t led_color_wake_bk = {0};
static TaskHandle_t g_breath_light_task_handle = NULL;
static TaskHandle_t change_light_task_handle = NULL;

static void change_light_task(void *arg)
{
    static uint32_t hue = 0;
    while (true) {
        /**< Write HSV values to LED */
        ESP_ERROR_CHECK(g_leds->set_hsv(g_leds, hue, 100, 100));
        vTaskDelay(pdMS_TO_TICKS(100));
        hue++;

        if (hue > 360) {/**< The maximum value of hue in HSV color space is 360 */
            hue = 0;
        }
    }
}

static void vibration_handle(void *arg)
{
    uint16_t h;
    uint8_t s;

    if (NULL != change_light_task_handle) {
        vTaskDelete(change_light_task_handle);    
        change_light_task_handle = NULL;
    }

    /**< Set a random color */
    h = esp_random() / 11930465;
    s = esp_random() / 42949673;
    s = s < 40 ? 40 : s;

    ESP_ERROR_CHECK(g_leds->set_hsv(g_leds, h, s, 100));
}


static void button_press_3sec_cb(void *arg)    //按键被长按3s后回调函数
{
    ESP_LOGW(TAG, "Restore factory settings");
    nvs_flash_erase();
    esp_restart();
}

static void configure_push_button(int gpio_num)   
{
    button_handle_t btn_handle = iot_button_create(gpio_num, 0);        //创建一个按键任务，并返回相应的handle

    if (btn_handle) {
        iot_button_add_on_press_cb(btn_handle, 3, button_press_3sec_cb, NULL);  
    }
}

static void udp_server_task(void *pvParameters)
{
    char rx_buffer[128];
    char addr_str[128];
    int addr_family = (int)AF_INET;
    int ip_protocol = 0;
    struct sockaddr_in6 dest_addr;

    while (1) {

        if (addr_family == AF_INET) {
            struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&dest_addr;
            dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY);
            dest_addr_ip4->sin_family = AF_INET;
            dest_addr_ip4->sin_port = htons(PORT);
            ip_protocol = IPPROTO_IP;
        }

        int sock = socket(addr_family, SOCK_DGRAM, ip_protocol);

        if (sock < 0) {
            ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
            break;
        }

        ESP_LOGI(TAG, "Socket created");

        int err = bind(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));

        if (err < 0) {
            ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
        }

        ESP_LOGI(TAG, "Socket bound, port %d", PORT);

        while (1) {

            ESP_LOGI(TAG, "Waiting for data");
            struct sockaddr_in6 source_addr; /**< Large enough for both IPv4 or IPv6 */
            socklen_t socklen = sizeof(source_addr);
            int len = recvfrom(sock, rx_buffer, sizeof(rx_buffer) - 1, 0, (struct sockaddr *)&source_addr, &socklen);

            /**< Error occurred during receiving */
            if (len < 0) {
                ESP_LOGE(TAG, "recvfrom failed: errno %d", errno);
                break;
            }
            /**< Data received */
            else {
                /**< Get the sender's ip address as string */
                if (source_addr.sin6_family == PF_INET) {
                    inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr.s_addr, addr_str, sizeof(addr_str) - 1);
                } else if (source_addr.sin6_family == PF_INET6) {
                    inet6_ntoa_r(source_addr.sin6_addr, addr_str, sizeof(addr_str) - 1);
                }

                rx_buffer[len] = 0; /**< Null-terminate whatever we received and treat like a string... */
                ESP_LOGI(TAG, "Received %d bytes from %s:", len, addr_str);

                cJSON *root = cJSON_Parse(rx_buffer);

                if (!root) {
                    printf("JSON format error:%s \r\n", cJSON_GetErrorPtr());
                } else {
                    cJSON *item = cJSON_GetObjectItem(root, "led");
                    int32_t red = cJSON_GetObjectItem(item, "red")->valueint;
                    int32_t green = cJSON_GetObjectItem(item, "green")->valueint;
                    int32_t blue = cJSON_GetObjectItem(item, "blue")->valueint;
                    cJSON_Delete(root);

                    if (NULL != change_light_task_handle) {
                        vTaskDelete(change_light_task_handle);    
                        change_light_task_handle = NULL;
                    }

                    if (red != g_red || green != g_green || blue != g_blue) {
                        g_red = red;
                        g_green = green;
                        g_blue = blue;
                        ESP_LOGI(TAG, "Light control: red = %d, green = %d, blue = %d", g_red, g_green, g_blue);
                        ESP_ERROR_CHECK(g_leds->set_rgb(g_leds, g_red, g_green, g_blue));
                    }
                }
            }
        }

        if (sock != -1) {
            ESP_LOGE(TAG, "Shutting down socket and restarting...");
            shutdown(sock, 0);
            close(sock);
        }
    }

    vTaskDelete(NULL);
}


static void breath_light_task(void *arg)
{
    uint8_t value = 0;
    uint8_t dir = 0;

    while (1) {
        ESP_ERROR_CHECK(g_leds->set_hsv(g_leds, 120, 100, value));
        vTaskDelay(pdMS_TO_TICKS(20));

        if (dir) {
            value--;

            if (value < 20) {
                dir = 0;
            }
        } else {
            value++;

            if (value >= 60) {
                dir = 1;
            }
        }
    }
}

static void sr_wake(void *arg)
{
    g_leds->get_rgb(g_leds, &led_color_wake_bk.r, &led_color_wake_bk.g, &led_color_wake_bk.b);
    /**< Turn on the breathing light */
    xTaskCreate(breath_light_task, "breath_light_task", 1024 * 2, NULL, configMAX_PRIORITIES - 1, &g_breath_light_task_handle);

}

static void sr_cmd(void *arg)
{
    if (NULL != g_breath_light_task_handle) {
        vTaskDelete(g_breath_light_task_handle);
        g_leds->set_rgb(g_leds, led_color_wake_bk.r, led_color_wake_bk.g, led_color_wake_bk.b);
        g_breath_light_task_handle = NULL;
    }

    if (NULL != change_light_task_handle) {
        vTaskDelete(change_light_task_handle);    
        change_light_task_handle = NULL;
    }

    int32_t cmd_id = (int32_t)arg;

    switch (cmd_id) {
        case 0:
        case 1:
        case 2:
            g_leds->set_rgb(g_leds, 120, 120, 120);
            break;

        case 3:
        case 4:
        case 5: {
            uint8_t r, g, b;
            g_leds->get_rgb(g_leds, &r, &g, &b);

            if (0 == r && 0 == g && 0 == b) {
                break;
            }

            g_leds->get_rgb(g_leds, &led_color.r, &led_color.g, &led_color.b);
            g_leds->set_rgb(g_leds, 0, 0, 0);
        }
        break;

        case 6: {
            uint32_t h, s, v;
            g_leds->get_hsv(g_leds, &h, &s, &v);
            /**< Set a random color */
            h = esp_random() / 11930465;
            s = esp_random() / 42949673;
            s = s < 40 ? 40 : s;
            ESP_ERROR_CHECK(g_leds->set_hsv(g_leds, h, s, v));
        }
        break;

        case 7:
        case 8: {
            uint32_t h, s, v;
            g_leds->get_hsv(g_leds, &h, &s, &v);

            if (v < 90) {
                v += 10;
            }

            g_leds->set_hsv(g_leds, h, s, v);
        }
        break;

        case 9:
        case 10: {
            uint32_t h, s, v;
            g_leds->get_hsv(g_leds, &h, &s, &v);

            if (v > 20) {
                v -= 10;
            }

            g_leds->set_hsv(g_leds, h, s, v);
        }
        break;

        case 11: {
            xTaskCreate(change_light_task, "change_light_task", 1024 * 2, NULL, configMAX_PRIORITIES - 2, &change_light_task_handle);
        }break;

        case 12: {                
            vTaskDelete(change_light_task_handle);    
            change_light_task_handle = NULL;
        }break;

        default:
            break;
    }
}

static void sr_cmd_exit(void *arg)
{
    if (NULL != g_breath_light_task_handle) {
        vTaskDelete(g_breath_light_task_handle);
        g_leds->set_rgb(g_leds, led_color_wake_bk.r, led_color_wake_bk.g, led_color_wake_bk.b);
        g_breath_light_task_handle = NULL;
    }
}

void app_main(void)
{
    static uint32_t work_CNT=0;

    /**< Initialize NVS */
    esp_err_t ret = nvs_flash_init();

    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }

    ESP_ERROR_CHECK(ret);

    configure_push_button(BOARD_GPIO_BUTTON);

    /**< configure led driver */
    led_rgb_config_t rgb_config = LED_RGB_DEFAULT_CONFIG(BOARD_GPIO_LED_R, BOARD_GPIO_LED_G, BOARD_GPIO_LED_B);
    g_leds = led_rgb_create(&rgb_config);

    if (!g_leds) {
        ESP_LOGE(TAG, "install LED driver failed");
        return;
    }

    /**< Turn on the breathing light */
    xTaskCreatePinnedToCore(breath_light_task, "breath_light_task", 1024 * 3, NULL, 5, &g_breath_light_task_handle, 1);

    /**< Initialize the BluFi */
    blufi_network_init();
    bool configured;
    blufi_is_configured(&configured);

    if (!configured)
    {
        blufi_start();
    }

    ESP_LOGI(TAG, "Wait for connect");
    blufi_wait_connection(portMAX_DELAY);

    if (!configured) {
        blufi_stop();
        
    }

    vTaskDelete(g_breath_light_task_handle);
    // static bool wifi_CON_STATUE=1;

    // if ( CONNECTED_BIT == 1 ) {
    //     ESP_LOGI(TAG, "Wifi is connected AP");  
    //     if ( wifi_CON_STATUE == 1 ) 
    //     {
    //         wifi_CON_STATUE = 0;
    //         ESP_ERROR_CHECK(g_leds->set_rgb(g_leds, 0, 0, 0));
    //     }     
            
    // }

    /**< It's connected. It's time to change the light to white */
    //  ESP_ERROR_CHECK(g_leds->set_rgb(g_leds, 120, 120, 120));
    ESP_ERROR_CHECK(g_leds->set_rgb(g_leds, 0, 0, 0));

    xTaskCreatePinnedToCore(udp_server_task, "udp_server_task", 1024 * 5, NULL, 5, NULL, 1);
    // xTaskCreate(udp_server_task, "udp_server_task", 1024 * 5, NULL, 5, &udp_server_task_handle);

    speech_recognition_init();

    // g_leds->set_rgb(g_leds, 120, 120, 120);
    g_leds->get_rgb(g_leds, &led_color.r, &led_color.g, &led_color.b);
    sr_handler_install(SR_CB_TYPE_WAKE, sr_wake, NULL);
    sr_handler_install(SR_CB_TYPE_CMD, sr_cmd, NULL);
    sr_handler_install(SR_CB_TYPE_CMD_EXIT, sr_cmd_exit, NULL);

    sensor_battery_init(BOARD_BAT_ADC_CHANNEL, BOARD_GPIO_BAT_CHRG, BOARD_GPIO_BAT_STBY);
    sensor_vibration_init(BOARD_GPIO_SENSOR_INT);
    sensor_vibration_triggered_register(vibration_handle, NULL);

    work_CNT++;
    if(work_CNT>65532)
    {
        work_CNT = 65532;
    }
    printf("main is running %d times\r\n",work_CNT);
}
